use crate::{client::RedisClient, resp::Command, RedisResult};

use super::SetMode;

impl RedisClient {
    pub async fn string_set(&self, key: &str, value: impl Into<String>) -> RedisResult<()> {
        let cmd = Command::new_with_key("SET", key).arg(value.into());
        self.send_command(cmd).await
    }
    pub async fn string_set_plus(
        &self,
        key: &str,
        value: impl Into<String>,
        mode: Option<SetMode>,
    ) -> RedisResult<()> {
        let mut cmd = Command::new_with_key("SET", key).arg(value.into());
        if let Some(set_mode) = mode {
            cmd.arg_append(set_mode);
        }
        self.send_command(cmd).await
    }

    pub async fn string_set_get(
        &self,
        key: &str,
        value: impl Into<String>,
        mode: Option<SetMode>,
        get_flag: bool,
        expire: Option<std::time::Duration>,
    ) -> RedisResult<()> {
        let mut cmd = Command::new_with_key("SET", key).arg(value.into());
        if let Some(set_mode) = mode {
            cmd.arg_append(set_mode);
        }
        if get_flag {
            cmd.arg_append("GET");
        }
        if let Some(dur) = expire {
            cmd.arg_append("PX").arg_append(dur.as_millis());
        } else {
            cmd.arg_append("KEEPTTL");
        }
        self.send_command(cmd).await
    }

    pub async fn string_get(&self, key: &str) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("GET", key);
        self.send_command(cmd).await
    }

    pub async fn string_get_del(&self, key: &str) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("GETDEL", key);
        self.send_command(cmd).await
    }

    pub async fn string_append(&self, key: &str, value: impl Into<String>) -> RedisResult<usize> {
        let cmd = Command::new_with_key("APPEND", key).arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn string_decr(&self, key: &str) -> RedisResult<isize> {
        let cmd = Command::new_with_key("DECR", key);
        self.send_command(cmd).await
    }

    pub async fn string_decr_by(&self, key: &str, decrement: isize) -> RedisResult<isize> {
        let cmd = Command::new_with_key("DECRBY", key).arg(decrement);
        self.send_command(cmd).await
    }

    pub async fn string_get_ex(
        &self,
        key: &str,
        expire: Option<std::time::Duration>,
    ) -> RedisResult<Option<String>> {
        let mut cmd = Command::new_with_key("GETEX", key);
        if let Some(dur) = expire {
            cmd.arg_append("PX").arg_append(dur.as_millis());
        } else {
            cmd.arg_append("PERSIST");
        }
        self.send_command(cmd).await
    }

    pub async fn string_get_range(
        &self,
        key: &str,
        start: isize,
        end: isize,
    ) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("GETRANGE", key).arg(start).arg(end);
        self.send_command(cmd).await
    }

    pub async fn string_incr(&self, key: &str) -> RedisResult<isize> {
        let cmd = Command::new_with_key("INCR", key);
        self.send_command(cmd).await
    }

    pub async fn string_incr_by(&self, key: &str, decrement: isize) -> RedisResult<isize> {
        let cmd = Command::new_with_key("INCRBY", key).arg(decrement);
        self.send_command(cmd).await
    }

    pub async fn string_incr_by_float(
        &self,
        key: &str,
        increment: f64,
    ) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("INCRBYFLOAT", key).arg(increment);
        self.send_command(cmd).await
    }

    pub async fn string_mget(&self, keys: &[&str]) -> RedisResult<Vec<Option<String>>> {
        let cmd = Command::new_with_keys("MGET", keys);
        self.send_command(cmd).await
    }

    pub async fn string_mset(
        &self,
        datas: Vec<(String, String)>,
    ) -> RedisResult<Vec<Option<String>>> {
        let mut cmd = Command::new("MSET");
        for (key, val) in datas.into_iter() {
            cmd.arg_append(key).arg_append(val);
        }
        self.send_command(cmd).await
    }

    pub async fn string_mset_nx(&self, datas: Vec<(String, String)>) -> RedisResult<isize> {
        let mut cmd = Command::new("MSETNX");
        for (key, val) in datas.into_iter() {
            cmd.arg_append(key).arg_append(val);
        }
        self.send_command(cmd).await
    }

    pub async fn string_set_range(
        &self,
        key: &str,
        len: usize,
        value: impl Into<String>,
    ) -> RedisResult<isize> {
        let cmd = Command::new_with_key("SETRANGE", key)
            .arg(len)
            .arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn string_strlen(&self, key: &str) -> RedisResult<isize> {
        let cmd = Command::new_with_key("STRLEN", key);
        self.send_command(cmd).await
    }

    pub async fn string_substr(&self, key: &str, start: usize, end: usize) -> RedisResult<String> {
        let cmd = Command::new_with_key("SUBSTR", key).arg(start).arg(end);
        self.send_command(cmd).await
    }
}
